﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MiniCPU.Entities
{
    /// <summary>
    /// Memory class.
    /// </summary>
    public class Memory : DataPropagator
    {
        #region Variables
        /// <summary>
        /// The memory collection.
        /// </summary>
        private Dictionary<Int32, LimitedData> memory;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="Memory"/> class.
        /// </summary>
        /// <param name="numberOfBits">The number of bits.</param>
        /// <param name="address">The address.</param>
        public Memory(int numberOfBits, LimitedData address)
            : base("Memory", 0, numberOfBits)
        {
            this.Write = new ControlPort("Memory:Write");
            this.Read = new ControlPort("Memory:Read");
            this.memory = new Dictionary<Int32, LimitedData>();
            this.Address = address;

            this.Address.DataChanged += new MiniCPU.Events.DataChangedEventHandler(this.RefreshData);
            this.Read.DataChanged += new MiniCPU.Events.DataChangedEventHandler(this.RefreshData);
            this.Write.DataChanged += new MiniCPU.Events.DataChangedEventHandler(this.RefreshData);
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the address.
        /// </summary>
        /// <value>The address.</value>
        public LimitedData Address { get; set; }

        /// <summary>
        /// Gets or sets the data on input.
        /// </summary>
        /// <value>The data on input.</value>
        public LimitedData DataOnInput { get; set; }

        /// <summary>
        /// Gets or sets the read control port.
        /// </summary>
        /// <value>The read control port.</value>
        public ControlPort Read { get; set; }

        /// <summary>
        /// Gets or sets the write control port.
        /// </summary>
        /// <value>The write control port.</value>
        public ControlPort Write { get; set; }
        #endregion

        #region Methods
        /// <summary>
        /// Loads the program.
        /// </summary>
        /// <param name="program">The program.</param>
        public void LoadProgram(Dictionary<Int32, LimitedData> program)
        {
            foreach (var e in program)
            {
                this.memory.Add(e.Key, e.Value);
            }
        }

        /// <summary>
        /// Refreshes the data.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="MiniCPU.Events.DataChangedEventArgs"/> instance containing the event data.</param>
        private void RefreshData(object sender, MiniCPU.Events.DataChangedEventArgs e)
        {
            if (this.Read.Value)
            {
                // Update output. UpdateValue will trigger the event.
                if (this.memory.ContainsKey(this.Address.IntValue))
                {
                    UpdateValue(this.memory[this.Address.IntValue].IntValue);
                }
                else
                {
                    UpdateValue(0);
                }
            }
            else if (this.Write.Value)
            {
                if (this.memory.ContainsKey(this.Address.IntValue))
                {
                    this.memory[this.Address.IntValue].IntValue = this.DataOnInput.IntValue;
                }
            }
        }
        #endregion
    }
}
